import { Inject, Injectable, Optional } from "@angular/core";
import {
  HttpEvent,
  HttpHandler,
  HttpInterceptor,
  HttpRequest,
  HttpResponse
} from "@angular/common/http";
import { Observable, of } from "rxjs";
import { RequestCache } from "./request-cache.service";
import { startWith, tap } from "rxjs/operators";
import { APP_CONFIG, AppConfig } from "../../app-config";

@Injectable()
export class CachingInterceptor implements HttpInterceptor {
  constructor(
    private cache: RequestCache,
    @Optional() @Inject(APP_CONFIG) private config: AppConfig
  ) {}

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    if (!isCacheable(req, this.config ? this.config.cacheableUrl : [])) {
      return next.handle(req);
    }

    const cachedResponse = this.cache.get(req);

    // cache -> refresh
    if (req.headers.get("x-refresh")) {
      const result$ = sendRequest(req, next, this.cache);
      return cachedResponse ? result$.pipe(startWith(cachedResponse)) : result$;
    }

    // cache or fetch
    return cachedResponse
      ? of(cachedResponse)
      : sendRequest(req, next, this.cache);
  }
}

/** is this request cacheable */
function isCacheable(
  req: HttpRequest<any>,
  cacheableUrl: string[] = []
): boolean {
  // let cacheable = req.method === "GET";

  for (const url of cacheableUrl) {
    if (req.urlWithParams.indexOf(url) > -1) {
      return true;
    }
  }

  return false;
}

/** send request to `next()` and cache response */
function sendRequest(
  req: HttpRequest<any>,
  next: HttpHandler,
  cache: RequestCache
) {
  return next.handle(req).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        cache.put(req, event);
      }
    })
  );
}
